import React, { Component } from "react";
import { Provider } from "mobx-react";
import { Context } from './internal';

/**
 * Props has Context, Props has TProps, Context has TProps
 */
export interface Props<TParams, TContext extends Context<TParams>> { readonly context: TContext, readonly params: TParams }

declare interface IContextCreator<TParams> {
    (props: TParams): Context<TParams>
}

declare interface Props2Params<TProps, TParams> {
    (props: TProps): TParams
}

export function assemble<TParams, TContext extends Context<TParams>, TProps extends Props<TParams, TContext>>(Comp: React.ComponentClass<TProps>, contextCreator: IContextCreator<TParams>, convert?: Props2Params<TProps, TParams>) {
    return class MobxComponent extends Component<TProps, { params: TParams | null }> {

        contexture: Context<TParams>;

        constructor(props: TProps) {
            super(props);
            this.state = { params: null };
            const params = convert ? convert(props) : props.params;
            this.contexture = contextCreator(params);
        }

        static getDerivedStateFromProps?(nextProps: TProps, prevState: any): any | null {
            const params = convert ? convert(nextProps) : {};
            return { params };
        }

        render() {
            const { contexture, state } = this;
            return (
                <Provider context={contexture}>
                    <Comp {...this.props} context={contexture} params={state.params} />
                </Provider>
            );
        }

        componentDidMount?(): void {
            const { contexture } = this;
            return contexture.componentDidMount && contexture.componentDidMount();
        }

        getSnapshotBeforeUpdate?(prevProps: TProps, prevState: any) {
            const { contexture } = this;
            return contexture.getSnapshotBeforeUpdate && contexture.getSnapshotBeforeUpdate(prevProps, prevState);
        }

        componentDidUpdate?(): void {
            const { contexture } = this;
            return contexture.componentDidUpdate && contexture.componentDidUpdate();
        }

        componentWillUnmount?(): void {
            const { contexture } = this;
            return contexture.componentWillUnmount && contexture.componentWillUnmount();
        }

        componentDidCatch?(error: any, info: any): void {
            const { contexture } = this;
            return contexture.componentDidCatch && contexture.componentDidCatch(error, info);
        }
    };
}
